package chapter06._03_InnerClassTest;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Instant;

/**
 * @Version:
 * @Description: 局部内部类的学习
 * @LastDate: 2020/10/2
 */
public class Test01_Introduction {

    public static void main(String[] args) {
        var clock = new TalkingClock(2000, true);
        clock.start();

        JOptionPane.showMessageDialog(null, "Quit?");
        System.exit(0);
    }
}

class TalkingClock {
    private int interval;
    private boolean beep;
    private static int outer = 0;

    public TalkingClock(int interval, boolean beep) {
        this.interval = interval;
        this.beep = beep;
    }

    public void start() {
        /*事实上下面这条语句是这样的
        * var lisener = new TimePrinter(this);
        * */
        //var lisener = this.new TimePrinter();//显式创建内部类对象 有效语句
        var lisener = new TimePrinter();
        var timer = new Timer(interval, lisener);
        timer.start();
    }

    /*
    * 虽然内部类位于外部类的内部
    * 但这并不意味着每个外部类实例化对象都有一个对应的内部类
    *
    * 你还可以把内部类修饰符改成private 这样只有外部类能创建内部类对象
    * 普通类是包可见或者公共可见的
    *
    * ps:这种是显式内部类 还有一种匿名内部类
    * */
    public class TimePrinter implements ActionListener {
        /*
        * 对于内部类的字段和方法 需要注意的有
        * static字段必须是final 且有初值
        * 原则上来说 不能有static方法
        * 但是如果使用了静态方法 内部类也必须改为static
        * 而且static inner class就变成只能访问外部类的静态字段和方法
        *
        * 关于匿名类只能访问final的原因:
        * 用R大回答里的原话说就是：
        * Java编译器实现的只是capture-by-value,并没有实现capture-by-reference。
        * 而只有后者才能保持匿名内部类和外部环境局部变量保持同步。
        * 但Java又不肯明说，只能粗暴地一刀切，就说既然内外不能同步，那就不许大家改外围的局部变量。
        * */
        //private static int time;// error

/*        public static void staticTest() {
            //System.out.println(beep);// error
            System.out.println(outer);// work
        }*/

        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("the time is " + Instant.ofEpochMilli(e.getWhen()));
            /*
            * TimePrinter并没有beep字段 但是却能访问
            * 也就是说 一个内部类可以访问外部类
            * 这是因为内部类总是有一个隐式引用字段指向外部类对象
            * 大概是这样 通过构造器 来引用外部类
            * public TimePrinter(TalkingClock clock) {
                outer = clock;
              }
            * */

            /*
            * 虽然程序帮你隐式引用 但事实上的语法更加复杂
            * 比如以下这条语句 如果采用外部引用写法 完整版本应当是
            * if (TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();
            * 构造器也可以如此
            * */
            if (beep) Toolkit.getDefaultToolkit().beep();
        }
    }
}
